/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.classdiagram.tabbedproperties.section;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.ParameterableElement;
import org.eclipse.uml2.uml.TemplateParameterSubstitution;

import com.cea.papyrus.core.commands.template.DeleteReferenceFromTemplateParameterSubstitutionCommand;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.editpart.IUMLElementEditPart;
import com.cea.papyrus.core.tabbedproperties.PropertyViewSection;
import com.cea.papyrus.core.tabbedproperties.composite.DecoratedTextComposite;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.classdiagram.commands.ElementReferencedByATemplateParameterSubstitutionCreateCommand;
import com.cea.papyrus.profile.ui.dialogs.ChooseSetAssistedDialog;
import com.cea.papyrus.umlutils.Element;
import com.cea.papyrus.umlutils.Package;

/**
 * The template signature section on the element tab.
 * This section is added only when a TemplateableElement owns a TemplateSignature
 * (cf. TemplateSignatureSectionFilter)
 */
public class ActualParameterSection extends PropertyViewSection {

	/**
	 * the composite of this section
	 */
	private DecoratedTextComposite actualParameterComposite;

	private com.cea.papyrus.umlutils.Package context ;
	
	
/**
 * {@inheritDoc}
 */	
	public void createControls(Composite parent,
			TabbedPropertySheetPage tabbedPropertySheetPage) {
		super.createControls(parent, tabbedPropertySheetPage);

		final TabbedPropertySheetPage sheetPage = tabbedPropertySheetPage ;

		actualParameterComposite = new DecoratedTextComposite("Actual") {
			
			public void editText() {
				TemplateParameterSubstitution currentTemplateParameterSubstitution = (TemplateParameterSubstitution)getElement().getUml2Element() ;
				if (!currentTemplateParameterSubstitution.getOwnedActuals().isEmpty()) {
					EditPart selection = null ;
					for (Iterator<EditPart> i=LookForElement.getAllOutlineEditParts().iterator() ; i.hasNext() ; ) {
						EditPart current = i.next() ;
						if (current.getModel() == currentTemplateParameterSubstitution.getOwnedActuals().get(0))
							selection = current ;
					}
					if (selection != null)
						sheetPage.selectionChanged(getPart(), new StructuredSelection(selection)) ;
				}
			}
			
			public void removeButtonPressed() {
				TemplateParameterSubstitution currentTemplateParameterSubstitution = (TemplateParameterSubstitution)this.getElement().getUml2Element() ;
				if (currentTemplateParameterSubstitution.getActuals().get(0) != null) {
					CommandStack stack = CommandStackUtils.getCommandStack();
					org.eclipse.gef.commands.Command command = new DeleteReferenceFromTemplateParameterSubstitutionCommand(currentTemplateParameterSubstitution, (ParameterableElement)currentTemplateParameterSubstitution.getActuals().get(0)) ;
					Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStackUtils");
					stack.execute(command);
					refresh() ;
				}
			}
			
			public void addButtonPressed() {
				
				TemplateParameterSubstitution currentTemplateParameterSubstitution = (TemplateParameterSubstitution)this.getElement().getUml2Element() ;
				org.eclipse.gef.commands.Command command = null ;
				CommandStack stack = CommandStackUtils.getCommandStack();
				EClass parameteredElementMetaclass = null ; // the metaclass of the parametered element of the formal parameter (used as a filter for the ChooseFilteredMetaclassDialog)
				parameteredElementMetaclass = currentTemplateParameterSubstitution.getFormal().getParameteredElement().eClass() ;
				if (currentTemplateParameterSubstitution.getFormal() instanceof ClassifierTemplateParameter) {
					if (((ClassifierTemplateParameter)currentTemplateParameterSubstitution.getFormal()).getConstrainingClassifier() != null) {
						parameteredElementMetaclass = ((ClassifierTemplateParameter)currentTemplateParameterSubstitution.getFormal()).getConstrainingClassifier().eClass() ;
					}
				}
				try {
					actualParameterComposite.dispose() ;
					ChooseActualParameterDialog dialog ;
					dialog = new ChooseActualParameterDialog(composite.getShell(), 
							"Possible classifiers:", 
							"Selected classifier:",
							parameteredElementMetaclass,
							currentTemplateParameterSubstitution,
							context) ;
					dialog.create();
					dialog.setFocus();
					dialog.open();
					if(dialog.getSelectedElement()!=null) {
						command = new ElementReferencedByATemplateParameterSubstitutionCreateCommand((ParameterableElement)dialog.getSelectedElement(),
								currentTemplateParameterSubstitution,
								Class.forName("org.eclipse.uml2.uml.UseCase")) ;
						Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
						stack.execute(command);
						refresh() ;
					}
				}
				catch (Exception e) {
					e.printStackTrace() ;
				}
//				TemplateParameter currentTemplateParameter = (TemplateParameter)this.getElement().getUml2Element() ;
//				
//				ChooseActualParameterDialog dialog = new ChooseActualParameterDialog(composite.getShell(), 
//															"Possible classifiers:", 
//															"Selected classifier:",
//															currentTemplateParameter.getParameteredElement().eClass(),
//															context) ; 
//				dialog.create();
//				dialog.setFocus();
//				dialog.open();
//				if(dialog.getSelectedElement()!=null) {
//					org.eclipse.gef.commands.Command command ;
//					CommandStack stack = LookForElement.getCommandStack();
//					Class elementClass = null ;
//					try {
//						String className = "org.eclipse.uml2.uml." + dialog.getSelectedElement().eClass().getName() ;
//						elementClass = Class.forName(className) ;
//					}
//					catch (Exception e) {
//						e.printStackTrace() ;
//					}
//					command = new ElementReferencedByATemplateParameterCreateCommand(dialog.getSelectedElement(), elementClass, currentTemplateParameter, ElementReferencedByATemplateParameterCreateCommand.DEFAULT) ;
//					Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
//					stack.execute(command);
//					refresh() ;
//					if ((graphicalElement != null )&&(graphicalElement instanceof TemplateParameterEditPart)) {
//						for (Iterator<EditPart> i = LookForElement.getAllEditParts(((EditPart)graphicalElement).getRoot()).iterator() ; i.hasNext() ; ) {
//							EditPart current = i.next() ;
//							if (current instanceof TemplateParameterEditPart) {
//								if (((TemplateParameterEditPart)current).getUmlElement() == currentTemplateParameter) {
//									TemplateParameterEditPart currentTemplateParameterEditPart = (TemplateParameterEditPart)current ;
//									UmlElementListener _adapter=new UmlElementListener((TemplateParameterEditPart)current) ;
//									((Notifier)currentTemplateParameter.getDefault()).eAdapters().add(_adapter) ;
//									current.refresh() ;
//								}
//							}
//						}
//					}
//				}
			}
			
			public void refresh() {
				if (text.isDisposed()) return ;
				TemplateParameterSubstitution currentTemplateParameterSubstitution = (TemplateParameterSubstitution)this.getElement().getUml2Element() ;
				if (!currentTemplateParameterSubstitution.getActuals().isEmpty()) {
					ParameterableElement actualParameter = (ParameterableElement)currentTemplateParameterSubstitution.getActuals().get(0) ;
					this.text.setText(((NamedElement)actualParameter).getQualifiedName()) ;
					if (currentTemplateParameterSubstitution.getOwnedActuals().contains(actualParameter)) {
						this.addButton.setEnabled(false) ;
						this.removeButton.setEnabled(false) ;
						this.text.setEnabled(false) ;
					}
					else {
						this.addButton.setEnabled(true) ;
						this.removeButton.setEnabled(true) ;
						this.text.setEnabled(true) ;
					}
				}
				else {
					this.text.setText("") ;
					this.addButton.setEnabled(true) ;
					this.removeButton.setEnabled(true) ;
					this.text.setEnabled(true) ;
				}
			}
		} ;
		actualParameterComposite.createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
	}

	/**
	 * {@inheritDoc}
	 */
	public void refresh() {
		actualParameterComposite.refresh();
	}

	/**
	 * {@inheritDoc}
	 */
	public void setInput(IWorkbenchPart part, ISelection selection) {
		super.setInput(part, selection);
		Assert.isTrue(selection instanceof IStructuredSelection);
		Object input = ((IStructuredSelection) selection).getFirstElement();
		if (input instanceof IUMLElementEditPart) {
			org.eclipse.uml2.uml.Element element = ((IUMLElementEditPart) input).getUmlElement();
        	if(element instanceof org.eclipse.uml2.uml.TemplateParameterSubstitution) {
        		com.cea.papyrus.umlutils.Element utilElement = new Element((org.eclipse.uml2.uml.TemplateParameterSubstitution)element);
        		if (utilElement.getUml2Element() instanceof org.eclipse.uml2.uml.Package)
        			this.context = new com.cea.papyrus.umlutils.Package((org.eclipse.uml2.uml.Package)utilElement.getUml2Element()) ;
        		else
        			this.context = new com.cea.papyrus.umlutils.Package(utilElement.getUml2Element().getNearestPackage()) ; 
        		actualParameterComposite.setElement(utilElement) ;
//        		actualParameterComposite.context = this.context ;
        	}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	public void dispose(){
		super.dispose();
		if(actualParameterComposite!=null)
			actualParameterComposite.dispose();
	}
	
	
	// ChooseSetAssistedDialog is specialized locally
	protected class ChooseActualParameterDialog extends ChooseSetAssistedDialog {
		
		private String selectedClassifierName ;
		
		private Map<String, org.eclipse.uml2.uml.Element> accessibleElementsMap ;
		
		private Package context ;
		
		public ChooseActualParameterDialog(Shell parentShell, String possibleText, String selectedText, EClass filter, TemplateParameterSubstitution templateParameterSubstitution, Package context){
			super(parentShell, possibleText, selectedText) ;
			this.context = context ;
			
			Set allAccessibleParameterableElements = new HashSet(templateParameterSubstitution.getTemplateBinding().getBoundElement().getOwnedElements()) ;
			
			if (templateParameterSubstitution.getFormal() instanceof ClassifierTemplateParameter) {
				if (context.getModel().getUml2Element()==null)
					allAccessibleParameterableElements.addAll(context.getAccessibleTypes(true));
				else
					allAccessibleParameterableElements.addAll(context.getModel().getAccessibleTypes(true));
			}

			List<org.eclipse.uml2.uml.Classifier> compatibleMetaclasses = LookForElement.getMetaclasses(filter, false, context.getUml2Element()) ;
			
			accessibleElementsMap = new HashMap<String, org.eclipse.uml2.uml.Element>() ;
			
			for (Iterator i = allAccessibleParameterableElements.iterator(); i.hasNext() ; ) {
				Object toto = i.next() ;
				org.eclipse.uml2.uml.Element current ;
				if (toto instanceof org.eclipse.uml2.uml.Element)
					current = (org.eclipse.uml2.uml.Element)toto ;
				else
					current = ((com.cea.papyrus.umlutils.Element)toto).getUml2Element() ;
				for (Iterator<org.eclipse.uml2.uml.Classifier> j = compatibleMetaclasses.iterator() ; j.hasNext() ;	) {
					Classifier currentMetaClass = j.next() ;
					if (current.eClass().getName().equals(currentMetaClass.getName())) {
						if(!accessibleElementsMap.containsValue(current)) {
							//test du cas s'il y a un constraining classifier
							if (templateParameterSubstitution.getFormal() instanceof ClassifierTemplateParameter) {
								ClassifierTemplateParameter ctp = (ClassifierTemplateParameter)templateParameterSubstitution.getFormal() ;
								if (ctp.getConstrainingClassifier() != null) {
									Classifier constrainingClassifier = ctp.getConstrainingClassifier() ;
									if (((Classifier)current).allParents().contains(constrainingClassifier)) {
										possibleElementList.addElement(((NamedElement)current).getName());
										accessibleElementsMap.put(((NamedElement)current).getName(), current) ;
									}
								}
								else {
									possibleElementList.addElement(((NamedElement)current).getName());
									accessibleElementsMap.put(((NamedElement)current).getName(), current) ;
								}
							}
							else {
								possibleElementList.addElement(((NamedElement)current).getName());
								accessibleElementsMap.put(((NamedElement)current).getName(), current) ;
							}
						}
					}
				}
			}
		}
		
		// create method is overrided so that up and down buttons are not visible
		// Only one metaclass can be selected, so these features are useless
		public void create() {
			super.create() ;
			downButton.setVisible(false) ;
			upButton.setVisible(false) ;
		}
			
		public void runActionAdd(Object element) {
			super.runActionAdd(element) ;
			selectedClassifierName = new String((String)element) ;
			this.addButton.setEnabled(false) ;
		}
		
		protected void runActionRemove(Object element){
			super.runActionRemove(element) ;
			this.addButton.setEnabled(true) ;
			this.selectedClassifierName = null ;
		}
	
		public void cancelPressed() {
			selectedClassifierName = null ;
			super.cancelPressed() ;
		}
		
		public void okPressed() {
			if (selectedClassifierName!=null)
				super.okPressed() ;
		}
		
		public org.eclipse.uml2.uml.Element getSelectedElement() {
			if (selectedClassifierName == null)
				return null ;
			else {
				return accessibleElementsMap.get(selectedClassifierName) ;
			}
		}
		
	}
	
}